/*
 * lea_index_hash.cc
 *
 *  Created on: Feb 7, 2014
 *      Author: jiang
 */



#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>


#include "utils.h"
#include "lea_types.h"
#include "lea_utility.h"

inline void init_table(Options opt,TableCellTypeSplitSeed **table)
{
	opt.kmer_table_len = pow(opt.alphabet_len,opt.index_parameter.kmer_len);
	(*table) =static_cast<TableCellTypeSplitSeed *>(calloc(opt.kmer_table_len,sizeof(TableCellTypeSplitSeed)));
	if((*table) == 0){
		fprintf(stderr,"[LEA_INDXE]Insufficient Memory!\n");
	}
}

//Returns the kmer(seed) length, given the length
//of the reference
inline uint32_t get_kmer_length(int64_t l_pac)
{
	//FIXME need more complex function
	uint32_t kmer_len;
	if(l_pac < 100000 )
		kmer_len = 8;
	else
		kmer_len = 12;
	return kmer_len;
}
static void get_key_value(const uint8_t *seq, uint32_t length, Combination mask_sites,uint64_t &key_value)
{
	uint32_t value = 0;

	/*
	for(uint j = 0; j < mask_sites.size(); ++j){
				fprintf(stderr,"%d ",mask_sites[j]);
	}
	fprintf(stderr,"\n");
	for(uint j = 0; j < length ; ++j){
			fprintf(stderr,"%d ",seq[j]);
	}
	fprintf(stderr,"\n");
	*/

	std::list<uint8_t> pure_kmer;
	std::list<uint8_t>::iterator iter;
	for(uint i = 0; i < length ; ++i)pure_kmer.push_back(seq[i]);
	iter = pure_kmer.begin();
	for(uint i = 0; i< mask_sites.size(); ++i){
		//fprintf(stderr,"%d ",mask_sites[i]);
		iter = pure_kmer.begin();
		advance (iter,mask_sites[i]-i) ;
		pure_kmer.erase(iter);
	}

	//fprintf(stderr,"\n");
	for (std::list<uint8_t>::iterator it=pure_kmer.begin(); it != pure_kmer.end(); ++it){
		//fprintf(stderr,"%d ", *it);
		value *= 4;
		value += (*it);
	}
	key_value = value;
}
//Returns a list of Key, for each Key it contains split_pattern_mapped_int and the kmer.
static int get_kmers(const Position position,const ReferenceInfo reference_info,const Options opt,std::vector<uint32_t> &kmer_values){
	uint8_t *buf;
	uint32_t length = opt.index_parameter.kmer_len+opt.index_parameter.split_pattern.user_split_distance * opt.index_parameter.split_pattern.user_split_num;

	Combination number_list;
	CombinationList  split_position_combination_list;
	buf = get_reference_region(position,length,reference_info);
	//fprintf(stderr, "ref region: %s  \n",int2bin(buf, NULL));
	//fprintf(stderr,"length %llu \n",length);
	debug_show_bit(buf,length);

	//The split should not too near to the end, here we limit the
	//the split position at least 2 bases far from each two end
	for (uint32_t i = 2; i < opt.index_parameter.kmer_len - 2 ; ++i) number_list.push_back(i);

	combinations(number_list, opt.index_parameter.split_pattern.user_split_num, split_position_combination_list);

	for(uint i = 0; i < split_position_combination_list.size(); ++i){
		uint64_t key_value;
		get_key_value(buf,length, split_position_combination_list[i],key_value);

		//FIXME so far, first 24bit is used for kmer, last 8bit is for split combinations
		key_value <<= 8;
		key_value += i;
		//kmers.
		kmer_values.push_back(key_value);
	}

	//#define   LEA_INDEX_HASH_DEBUG
	#ifdef LEA_INDEX_HASH_DEBUG
	for(uint i = 0; i < split_position_combination_list.size(); ++i){
		for(uint j = 0; j < split_position_combination_list[i].size(); ++j){
			fprintf(stderr,"%d ",split_position_combination_list[i][j]);
		}
		fprintf(stderr,"\n");
	}
	#endif

	free(buf);
	return 0;
}

int static build_table(ReferenceInfo reference_info,Options opt, TableCellTypeSplitSeed* kmer_position_table)
{
	init_table(opt,&kmer_position_table);
	uint64_t pos, length;
	std::vector<uint32_t> kmer_values;

	//Only index forward sequence
	if (! opt.index_parameter.index_reverse_complement)
	{
	  length = reference_info.contain_reverse_complement ? static_cast<uint64_t>(reference_info.l_pac/2) : static_cast<uint64_t>(reference_info.l_pac);

	  //for each position, generate sliced kmers
	  for(pos = 0; pos < length - opt.index_parameter.kmer_len; pos++){
		  get_kmers(pos,reference_info,opt, kmer_values);
		  for(uint i = 0; i < kmer_values.size(); ++i) {
			  //fprintf(stderr,"%llu\n",kmers[i]);
			  kmer_position_table[kmer_values[i]].position_num += 1;
			  //only count the position now
		  }


	  }
	//#define  LEA_INDEX_HASH_BUILD_TABLE_DEBUG
	#ifdef LEA_INDEX_HASH_BUILD_TABLE_DEBUG
	for(pos = 0; pos < length - opt.index_parameter.kmer_len; pos++){
			  kmer_position_table].position_num += 1;
			  //only count the position now
		  }
	}
	#endif
	}
	return 0;
}


int static build_table(TableCellTypeOther **kmer_position_table)
{
	return 0;
}

//Returns success or not. Uses array to build the index table
int lea_index_hash(char *indexFile,Options opt)
{
	clock_t t;
	int forward_only = 0, ret;

	ReferenceInfo reference_info;

	//Parses reference in FASTA file
	gzFile fp = xzopen(indexFile, "r");
	reference_info.l_pac  = bns_fasta2bntseq(fp, indexFile, forward_only);
	reference_info.contain_reverse_complement = true;
	reference_info.bns = bns_restore(indexFile);
	reference_info.pac =  static_cast<uint8_t *>(calloc(reference_info.bns->l_pac / 4 + 1, 1));
	if (reference_info.pac == 0) {
			fprintf(stderr, "[LEA_INDEX] Insufficient Memory!\n");
			exit(1);
	}
	fread(reference_info.pac, 1, reference_info.bns->l_pac / 4 + 1, reference_info.bns->fp_pac);

	//Adjusts parameter based on reference length
	opt.index_parameter.kmer_len = get_kmer_length(reference_info.l_pac);
	if(opt.index_parameter.kmer_len > 12) fprintf(stderr,"error! kmer length too long. Max is 12.\n");
	opt.index_parameter.index_reverse_complement = false;
	opt.index_parameter.distinct_level = 2;
	opt.index_parameter.split_pattern.user_split_distance = 1;
	opt.index_parameter.split_pattern.user_split_num = 2;

	//Builds the kmer position mapping table
	TableCellTypeSplitSeed *kmer_position_table;
	ret = build_table(reference_info,opt,kmer_position_table);
	free(kmer_position_table);
	return 0;
}
